(ns clojure-demo.protocol
  (:use [clojure.java.io]))

(set! *warn-on-reflection* true)

;示例：矩阵协议。

(defprotocol Matrix
    "Protocol for working with 2d datastructures."
    (lookup [matrix i j])
    (update [matrix i j value])
    (rows [matrix])
    (cols [matrix])
    (dims [matrix]))

;让向量实现矩阵协议。
(extend-protocol Matrix
    clojure.lang.IPersistentVector
    (lookup [vov i j]
        (get-in vov [i j]))
    (update [vov i j value]
        (assoc-in vov [i j] value))
    (rows [vov]
        (seq vov))
    (cols [vov]
        (apply map vector vov))
    (dims [vov]
        [(count vov) (count (first vov))]))

;扩展到nil。
(extend-protocol Matrix
    nil
    (lookup [x i j])
    (update [x i j value])
    (rows [x] [])
    (cols [x] [])
    (dims [x] [0 0]))
    (lookup nil 5 5)
;= nil

(dims nil)
;= [0 0]

;让java数组实现矩阵协议。
(extend-protocol Matrix
    (Class/forName "[[D") 
    (lookup [matrix i j]
        (aget matrix i j))
    (update [matrix i j value] 
        (let [clone (aclone matrix)]
            (aset clone i
                (doto (aclone (aget clone i))
                    (aset j value)))
            clone))
    (rows [matrix]
        (map vec matrix))
    (cols [matrix]
        (apply map vector matrix))
    (dims [matrix]
        (let [rs (count matrix)]
            (if (zero? rs)
                [0 0]
                [rs (count (aget matrix 0))]))))

(def matrix (make-array Double/TYPE 2 3))
;= #'user/matrix

(rows matrix)
;= ([0.0 0.0 0.0]
;= [0.0 0.0 0.0])

(rows (update matrix 1 1 3.4))
;= ([0.0 0.0 0.0]
;= [0.0 3.4 0.0])

(lookup (update matrix 1 1 3.4) 1 1)
;= 3.4

(cols (update matrix 1 1 3.4))
;= ([0.0 0.0]
;= [0.0 3.4]
;= [0.0 0.0])

(dims matrix)
;= [2 3]